﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;

namespace Td.Additional.ComponentModel
{
    /// <summary>
    /// Implementation of IDataErrorInfo interface
    /// </summary>
    public abstract class DataErrorInfo : IDataErrorInfo
    {
        #region Additional Member

        /// <summary>
        /// Gets or sets a value indicating whether [initialize automatic].
        /// </summary>
        /// <value><c>true</c> if [initialize automatic]; otherwise, <c>false</c>.</value>
        public bool InitializeAutomatic { get; set; }

        /// <summary>
        /// True, if instance is initialized, otherwise false.
        /// </summary>
        private bool initialized = false;

        /// <summary>
        /// List with property names and corresponding proprety check attributes.
        /// </summary>
        public SortedDictionary<String, List<IPropertyCheckAttribute>> Checks;

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        private void Initialize()
        {
            if (initialized || !InitializeAutomatic)
                return;
            Checks = new SortedDictionary<string, List<IPropertyCheckAttribute>>();
            Type type = GetType();
            foreach (var property in type.GetProperties())
            {
                foreach (var attribute in property.GetCustomAttributes(typeof(IPropertyCheckAttribute), false))
                {
                    IPropertyCheckAttribute attr = attribute as IPropertyCheckAttribute;
                    if (attr != null)
                    {
                        if (!Checks.ContainsKey(property.Name))
                            Checks.Add(property.Name, new List<IPropertyCheckAttribute>());
                        Checks[property.Name].Add(attr);
                    }
                }
            }
            initialized = true;
        }

        #endregion

        #region IDataErrorInfo Member

        /// <summary>
        /// Gets an error message indicating what is wrong with this object.
        /// </summary>
        /// <value>The error message.</value>
        /// <returns>
        /// An error message indicating what is wrong with this object. The default is an empty string ("").
        /// </returns>
        public string Error
        {
            get { return this[null]; }
        }

        /// <summary>
        /// Gets the error message indicating what is wrong with the named property.
        /// </summary>
        /// <value>The error message.</value>
        public String this[string columnName]
        {
            get
            {
                Initialize();
                Type type = GetType();
                var result = String.Empty;
                if (String.IsNullOrEmpty(columnName))
                    foreach (var checkItem in Checks)
                        result += this[checkItem.Key];
                else
                    foreach (var item in Checks[columnName])
                    {
                        if (!String.IsNullOrEmpty(result))
                            result += System.Environment.NewLine;
                        result += item.Check(this, 
                            type.GetProperty(columnName),
                            type.InvokeMember(columnName,
                            BindingFlags.GetProperty,
                            null,
                            this,
                            null));
                    }
                return result;
            }
        }

        #endregion
    }
}